Skip to content
This repository was archived by the owner on Jun 1, 2022. It is now read-only.

Engine: Add optics to core#50

Open
joeltio wants to merge 5 commits into
masterfrom
feat/engine-optics
Open

Engine: Add optics to core#50
joeltio wants to merge 5 commits into
masterfrom
feat/engine-optics

Conversation

@joeltio

@joeltio joeltio commented Feb 12, 2021

Copy link
Copy Markdown
Collaborator

This PR adds:

  • Getter: a view-only function
  • Lens: getter and setter

These are needed for array index operation. This PR does not introduce any usage of the optics, but it is intended to be used to replace passing and recreating Value in graphInterpreter.cpp.

@joeltio joeltio added feature New feature or request engine Area: Engine labels Feb 12, 2021
@mrzzy mrzzy added this to the v0.2 milestone Feb 13, 2021
@mrzzy

mrzzy commented Feb 20, 2021

Copy link
Copy Markdown
Collaborator

It's not very clear why there is a need to split the PR here:

  • This code seems to be in isolation (ie not called by anything other than unit tests).
  • The code is also not exercised by the E2E test will gives me less confidence that it works.

Maybe you could integrate lens into the existing get/setting attributes so that it exercised by E2E

@mrzzy mrzzy left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed. Honestly this has been really difficult to review.

  • Moving forward I hope we can have solutions that are simple & elegant, instead of being complex for the sake for of complex for code readability and maintainability down the road.
  • As mentioned in an above comment, Code should be used instead of left hanging unused: without seeing how its used in the Engine, its hard to understand how this code is used, whether this complexity is needed, and whether this code works (should be exercised by E2E tests).

Comment on lines +12 to +34
template <size_t N = 0, class... OtherArgs>
static constexpr bool _as() {
// constexpr is required here because N is a non-type template
// argument
if constexpr (N == sizeof...(OtherArgs)) {
return true;
} else if (std::is_same_v<
std::remove_reference_t<
std::tuple_element_t<N, std::tuple<OtherArgs...>>>,
std::remove_reference_t<
std::tuple_element_t<N, std::tuple<Args...>>>>) {
return _as<N + 1, OtherArgs...>();
} else {
return false;
}
}

// Alias to the recursive comparison constexpr function
template <class... OtherArgs>
static constexpr bool as() {
return _as<0, OtherArgs...>();
}
};

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible to convert this into a C++ 17 fold expression?

: getter(getter) {}

template <class... RArgs>
requires(is_same_args<Args...>::template as<RArgs...>()) Return

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming that I understand is_same_args<>::as<>() correctly, this defines a requirement that RArgs contains the exact same set of types as Args.
In that case, why can't we just use Args instead of having both RArgs & Args and then comparing if they are equal?

// does the same thing.

template <class Return, class... Args>
class Getter {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hard to understand Getter by name. From what i read in code it seems to be like a SQL SELECT statement where you can perform computations from selected values:

SELECT 
# equivalent to the lambda/function passed to Getter() constructor 
maxAge - age 
FROM 
# values passed via Getter.get()
getter_values

In that case, maybe selector/prism/transform might be a better name than Getter?

Comment on lines +67 to +68
// Make a copy of the getter so that if the current Getter is destroyed
// The returned getter is still usable

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In that case, why can't we have a pass-by-value copy constructor for Getter to be always assured a copy of the getter function for every Getter.

class Getter {
protected:
public:
const std::function<Return(std::remove_reference_t<Args>&&...)> getter;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming this function getter is too close to Getter, making the code hard to follow.

Comment on lines +110 to +114
typedef std::function<void(Return&, Return&&)> SetterFn;
// When writing the getter as a lambda, it is important to specify the
// arguments as rvalue references and the return type as an lvalue
// reference.
typedef std::function<Return&(Args&&...)> GetterFn;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what the point of making these private is.

};

template <class Return, class... Args>
class Lens {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like a Lens is a read/write version of a Getter, although it seems that they do not share a codebase.

  • why does is the readonly part of Lens not implemented in terms of Getter?
  • why do we need the setters in lens? why cant all value transforms be implemented in terms of a getter + = assignment and eliminate setters?.

Naming: Name wise they seem to completely unrelated.

Without being used by actual code by the Engine codebase, I don't understand we need a Lens.

&& std::is_same_v<std::remove_reference_t<RReturn>, Return> void set(
RReturn&& val, RArgs&&... args) const {
auto& ref = get(std::forward<RArgs>(args)...);
this->setter(ref, static_cast<std::remove_reference_t<RReturn>&&>(val));

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consistency. Choose whether you would like to use this-> or not. Earlier getter was used without this->.

Comment on lines +85 to +93
template <class... NewArgs, class Fn>
// Require that the current getter only needs one argument
requires(sizeof...(Args) == 1) &&
// Require that the type Fn is something invocable with the NewArgs and
// returns the first argument
std::is_invocable_r_v<
std::tuple_element_t<0, std::tuple<Args...>>, Fn,
NewArgs...> Getter<Return,
NewArgs...> precompose(Fn&& precomposeGetter)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use typedef/using so that function declarations are easier to read.

template <class NewReturn>
Lens<NewReturn, Args...> compose(
const Lens<NewReturn, Return>& otherLens) const {
std::function<NewReturn&(Args && ...)> get =

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose another name. This one overshadows get() the method which might not be intended.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

engine Area: Engine feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants